package com.cw.lang.common.framework.aspect;

import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Set;

import javax.validation.ConstraintViolation;
import javax.validation.Validator;
import javax.validation.executable.ExecutableValidator;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.Order;
import org.springframework.util.CollectionUtils;

import com.cw.lang.common.constants.SymbolConstants;
import com.cw.lang.common.enums.EnumResponseType;
import com.cw.lang.common.exception.CheckException;
import com.cw.lang.common.framework.utils.SpringContextUtil;
import com.google.common.collect.Sets;

import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;

/**
 * controller业务参数校验
 *
 * @author chenw
 * @version $$Id: ControllerValidatorAspect.java, v 0.1 2018/11/1 1:46 PM chenw Exp $$
 */
@Order(3000)
@Aspect
@Getter
@Setter
@Slf4j
public class ControllerBizParameterValidatorAspect {

    private Validator validator;

    @Around("execution(public * com..controller..*Controller.*(..))")
    public Object beforeMethod(ProceedingJoinPoint jp) throws Throwable {
        Signature signature = jp.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;

        Method method = methodSignature.getMethod();

        Object[] args = jp.getArgs();
        ExecutableValidator executableValidator = validator.forExecutables();
        Set<ConstraintViolation<Object>> constraintViolations = Sets.newHashSet();
        // 先校验方法入参上的参数列表
        constraintViolations.addAll(
            executableValidator.validateParameters(SpringContextUtil.getBean(method.getDeclaringClass()), method,
                args));

        if (CollectionUtils.isEmpty(constraintViolations)) {
            // 校验没有带@Valid的对象
            Arrays.stream(args)
                .forEach(input -> {
                    if (input != null) {
                        constraintViolations.addAll(validator.validate(input));
                    }
                });
        }

        //noinspection Duplicates
        if (!CollectionUtils.isEmpty(constraintViolations)) {
            StringBuilder sb = new StringBuilder();
            // 仅展示错误message
            constraintViolations.forEach(input -> sb.append(input.getMessage())
                .append(SymbolConstants.COMMA));
            String errorMessage = sb.substring(0, sb.length() - 1);
            log.error("校验不通过: [{}]", errorMessage);
            throw new CheckException(EnumResponseType.VALIDATE_ERR.getCode(), errorMessage);
        }
        return jp.proceed();
    }
}
